﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace DARE
{
    /// <summary>
    /// A 6-planes textured box that defines a sky box.
    /// To set a skybox, you only have to call the `Render3D.SkyMgr.CreateSkybox(string name, string assetName)`.
    /// 
    /// </summary>
    [AttrTypeDependentSerializable(typeof(DARE.Save.DataStructures.CSkyBoxData))]
    public class CSkybox: ASky
    {

        #region fields

        private Texture2D m_top;
        private Texture2D m_bottom;
        private Texture2D m_front;
        private Texture2D m_back;
        private Texture2D m_left;
        private Texture2D m_right;

        #endregion

        #region properties

        public Texture2D Top
        {
            get
            {
                return m_top;
            }
            set
            {
                m_top = value;
                Color[] topTexture = new Color[m_top.Width * m_top.Height];
                m_top.GetData(topTexture);
                (m_texture as TextureCube).SetData(CubeMapFace.PositiveY, topTexture);
            }
        }

        public Texture2D Bottom
        {
            get
            {
                return m_bottom;
            }
            set
            {
                m_bottom = value;
                Color[] bottomTexture = new Color[m_bottom.Width * m_bottom.Height];
                m_bottom.GetData(bottomTexture);
                (m_texture as TextureCube).SetData(CubeMapFace.NegativeY, bottomTexture);
            }
        }

        public Texture2D Front
        {
            get
            {
                return m_front;
            }
            set
            {
                m_front = value;
                Color[] frontTexture = new Color[m_front.Width * m_front.Height];
                m_front.GetData(frontTexture);
                (m_texture as TextureCube).SetData(CubeMapFace.PositiveZ, frontTexture);
            }
        }

        public Texture2D Back
        {
            get
            {
                return m_back;
            }
            set
            {
                m_back = value;
                Color[] backTexture = new Color[m_back.Width * m_back.Height];
                m_back.GetData(backTexture);
                (m_texture as TextureCube).SetData(CubeMapFace.NegativeZ, backTexture);
            }
        }

        public Texture2D Left
        {
            get
            {
                return m_left;
            }
            set
            {
                m_left = value;
                Color[] leftTexture = new Color[m_left.Width * m_left.Height];
                m_left.GetData(leftTexture);
                (m_texture as TextureCube).SetData(CubeMapFace.NegativeX, leftTexture);
            }
        }

        public Texture2D Right
        {
            get
            {
                return m_right;
            }
            set
            {
                m_right = value;
                Color[] rightTexture = new Color[m_right.Width * m_right.Height];
                m_right.GetData(rightTexture);
                (m_texture as TextureCube).SetData(CubeMapFace.PositiveX, rightTexture);
            }
        }

        #endregion

        #region ctor

        private void ctor()
        {
            
#if WINDOWS_PHONE
            m_effect = new EnvironmentMapEffect(DARE.CDare.Instance.GraphicsDevice);
#else
            m_effect = CResourceMgr.Load<Effect>(DARE.Properties.Resources.Skies, "Shaders/Skies");// IDEEEEEM
#endif
            m_technique = "CubeMapping";
        }

        ///// <summary>
        ///// Construcs the skybox with its render3D and a string that contains the cube map name.
        ///// </summary>
        //public CSkybox(CRender3D render3D, string cubeMapName)
        //    : base(render3D)
        //{
        //    m_texture = CResourceMgr.Load<TextureCube>(cubeMapName);
        //    m_top = CreateTextureFromTextureCube(CubeMapFace.PositiveY, m_texture as TextureCube);
        //    m_bottom = CreateTextureFromTextureCube(CubeMapFace.NegativeY, m_texture as TextureCube);
        //    m_front = CreateTextureFromTextureCube(CubeMapFace.PositiveZ, m_texture as TextureCube);
        //    m_back = CreateTextureFromTextureCube(CubeMapFace.NegativeZ, m_texture as TextureCube);
        //    m_left = CreateTextureFromTextureCube(CubeMapFace.NegativeX, m_texture as TextureCube);
        //    m_right = CreateTextureFromTextureCube(CubeMapFace.PositiveX, m_texture as TextureCube);
        //    ctor();
        //}

        /// <summary>
        /// Construcs the skybox with its render3D and a TextureCube that contains the cube map texture.
        /// </summary>
        public CSkybox(CRender3D render3D, TextureCube cubeMap)
            : base(render3D)
        {
            m_texture = cubeMap;
            m_top = CreateTextureFromTextureCube(CubeMapFace.PositiveY, m_texture as TextureCube);
            m_bottom = CreateTextureFromTextureCube(CubeMapFace.NegativeY, m_texture as TextureCube);
            m_front = CreateTextureFromTextureCube(CubeMapFace.PositiveZ, m_texture as TextureCube);
            m_back = CreateTextureFromTextureCube(CubeMapFace.NegativeZ, m_texture as TextureCube);
            m_left = CreateTextureFromTextureCube(CubeMapFace.NegativeX, m_texture as TextureCube);
            m_right = CreateTextureFromTextureCube(CubeMapFace.PositiveX, m_texture as TextureCube);
            ctor();
        }

        /// <summary>
        /// Create a skybox with 6 textures for the faces.
        /// </summary>
        /// <param name="sky">
        /// The path of the 6 textures (the common name, for example if the names are
        /// "cloudy_sky_UP.png", "cloudy_sky_DN.png", "cloudy_sky_LF.png", "cloudy_sky_RT.png", "cloudy_sky_BK.png", "cloudy_sky_FR.png" 
        /// the skypath to be given is "cloudy_sky".
        /// Each texture has to be named with a suffix:
        /// For the left one : "_LF".
        /// For the right one : "_RT".
        /// For the top one : "_UP".
        /// For the bottom one : "_DN".
        /// For the front one : "_FR".
        /// For the back one : "_BK".
        /// </param>
        public CSkybox(CRender3D render3D, string sky)
            : base(render3D)
        {
            string top = sky + "_UP";
            string bottom = sky + "_DN";
            string front = sky + "_FR"; 
            string back = sky + "_BK";
            string left = sky + "_LF";
            string right = sky + "_RT";

            Color[] topTexture = new Color[CResourceMgr.Load<Texture2D>(top).Width * CResourceMgr.Load<Texture2D>(top).Height];
            m_top = CResourceMgr.Load<Texture2D>(top);
            m_top.GetData(topTexture);
            Color[] bottomTexture = new Color[CResourceMgr.Load<Texture2D>(bottom).Width * CResourceMgr.Load<Texture2D>(bottom).Height];
            m_bottom = CResourceMgr.Load<Texture2D>(bottom);
            m_bottom.GetData(bottomTexture);
            Color[] frontTexture = new Color[CResourceMgr.Load<Texture2D>(front).Width * CResourceMgr.Load<Texture2D>(front).Height];
            m_front = CResourceMgr.Load<Texture2D>(front);
            m_front.GetData(frontTexture);
            Color[] backTexture = new Color[CResourceMgr.Load<Texture2D>(back).Width * CResourceMgr.Load<Texture2D>(back).Height];
            m_back = CResourceMgr.Load<Texture2D>(back);
            m_back.GetData(backTexture);
            Color[] leftTexture = new Color[CResourceMgr.Load<Texture2D>(left).Width * CResourceMgr.Load<Texture2D>(left).Height];
            m_left = CResourceMgr.Load<Texture2D>(left);
            m_left.GetData(leftTexture);
            Color[] rightTexture = new Color[CResourceMgr.Load<Texture2D>(right).Width * CResourceMgr.Load<Texture2D>(right).Height];
            m_right = CResourceMgr.Load<Texture2D>(right);
            m_right.GetData(rightTexture);
            TextureCube tc = new TextureCube(CDare.Instance.GraphicsDevice, CResourceMgr.Load<Texture2D>(top).Width, true, SurfaceFormat.Color);
            tc.SetData(CubeMapFace.PositiveY, topTexture);
            tc.SetData(CubeMapFace.NegativeY, bottomTexture);
            tc.SetData(CubeMapFace.PositiveZ, frontTexture);
            tc.SetData(CubeMapFace.NegativeZ, backTexture);
            tc.SetData(CubeMapFace.NegativeX, leftTexture);
            tc.SetData(CubeMapFace.PositiveX, rightTexture);
            m_texture = tc;
            ctor();
        }

        /// <summary>
        /// Construcs the skybox with its render3D and 6 strings that contains the 6 map name.
        /// </summary>
        public CSkybox(CRender3D render3D, string top, string bottom, string front, string back, string left, string right)
            : base(render3D)
        {
            Color[] topTexture = new Color[CResourceMgr.Load<Texture2D>(top).Width * CResourceMgr.Load<Texture2D>(top).Height];
            m_top = CResourceMgr.Load<Texture2D>(top);
            m_top.GetData(topTexture);
            Color[] bottomTexture = new Color[CResourceMgr.Load<Texture2D>(bottom).Width * CResourceMgr.Load<Texture2D>(bottom).Height];
            m_bottom = CResourceMgr.Load<Texture2D>(bottom);
            m_bottom.GetData(bottomTexture);
            Color[] frontTexture = new Color[CResourceMgr.Load<Texture2D>(front).Width * CResourceMgr.Load<Texture2D>(front).Height];
            m_front = CResourceMgr.Load<Texture2D>(front);
            m_front.GetData(frontTexture);
            Color[] backTexture = new Color[CResourceMgr.Load<Texture2D>(back).Width * CResourceMgr.Load<Texture2D>(back).Height];
            m_back = CResourceMgr.Load<Texture2D>(back);
            m_back.GetData(backTexture);
            Color[] leftTexture = new Color[CResourceMgr.Load<Texture2D>(left).Width * CResourceMgr.Load<Texture2D>(left).Height];
            m_left = CResourceMgr.Load<Texture2D>(left);
            m_left.GetData(leftTexture);
            Color[] rightTexture = new Color[CResourceMgr.Load<Texture2D>(right).Width * CResourceMgr.Load<Texture2D>(right).Height];
            m_right = CResourceMgr.Load<Texture2D>(right);
            m_right.GetData(rightTexture);
            TextureCube tc = new TextureCube(CDare.Instance.GraphicsDevice, CResourceMgr.Load<Texture2D>(top).Width, true, SurfaceFormat.Color);
            tc.SetData(CubeMapFace.PositiveY, topTexture);
            tc.SetData(CubeMapFace.NegativeY, bottomTexture);
            tc.SetData(CubeMapFace.PositiveZ, frontTexture);
            tc.SetData(CubeMapFace.NegativeZ, backTexture);
            tc.SetData(CubeMapFace.NegativeX, leftTexture);
            tc.SetData(CubeMapFace.PositiveX, rightTexture);
            m_texture = tc;
            ctor();
        }

        /// <summary>
        /// Construcs the skybox with its render3D and 6 Texture2D that contains the 6 map Textures.
        /// </summary>
        public CSkybox(CRender3D render3D, Texture2D top, Texture2D bottom, Texture2D front, Texture2D back, Texture2D left, Texture2D right)
            : base(render3D)
        {
            m_top = top;
            Color[] topTexture = new Color[top.Width * top.Height];
            top.GetData(topTexture);
            m_bottom = bottom;
            Color[] bottomTexture = new Color[bottom.Width * bottom.Height];
            bottom.GetData(bottomTexture);
            m_front = front;
            Color[] frontTexture = new Color[front.Width * front.Height];
            front.GetData(frontTexture);
            m_back = back;
            Color[] backTexture = new Color[back.Width * back.Height];
            back.GetData(backTexture);
            m_left = left;
            Color[] leftTexture = new Color[left.Width * left.Height];
            left.GetData(leftTexture);
            m_right = right;
            Color[] rightTexture = new Color[right.Width * right.Height];
            right.GetData(rightTexture);
            TextureCube tc = new TextureCube(CDare.Instance.GraphicsDevice, top.Width * top.Height, true, SurfaceFormat.Color);
            tc.SetData(CubeMapFace.PositiveY, topTexture);
            tc.SetData(CubeMapFace.NegativeY, bottomTexture);
            tc.SetData(CubeMapFace.PositiveZ, frontTexture);
            tc.SetData(CubeMapFace.NegativeZ, backTexture);
            tc.SetData(CubeMapFace.NegativeX, leftTexture);
            tc.SetData(CubeMapFace.PositiveX, rightTexture);
            m_texture = tc;
            m_top = CreateTextureFromTextureCube(CubeMapFace.PositiveY, m_texture as TextureCube);
            m_bottom = CreateTextureFromTextureCube(CubeMapFace.NegativeY, m_texture as TextureCube);
            m_front = CreateTextureFromTextureCube(CubeMapFace.PositiveZ, m_texture as TextureCube);
            m_back = CreateTextureFromTextureCube(CubeMapFace.NegativeZ, m_texture as TextureCube);
            m_left = CreateTextureFromTextureCube(CubeMapFace.NegativeX, m_texture as TextureCube);
            m_right = CreateTextureFromTextureCube(CubeMapFace.PositiveX, m_texture as TextureCube);
            m_top = CreateTextureFromTextureCube(CubeMapFace.PositiveY, m_texture as TextureCube);
            m_bottom = CreateTextureFromTextureCube(CubeMapFace.NegativeY, m_texture as TextureCube);
            m_front = CreateTextureFromTextureCube(CubeMapFace.PositiveZ, m_texture as TextureCube);
            m_back = CreateTextureFromTextureCube(CubeMapFace.NegativeZ, m_texture as TextureCube);
            m_left = CreateTextureFromTextureCube(CubeMapFace.NegativeX, m_texture as TextureCube);
            m_right = CreateTextureFromTextureCube(CubeMapFace.PositiveX, m_texture as TextureCube);
            ctor();
        }

        #endregion
 
        #region methods

        private Texture2D CreateTextureFromTextureCube(CubeMapFace face, TextureCube texture)
        {
            Texture2D t = new Texture2D(CDare.Instance.GraphicsDevice, texture.Size, texture.Size);
            Color[] c = new Color[texture.Size * texture.Size];
            texture.GetData(face, c);
            t.SetData(c);
            return t;
        }

        protected override void  CreateModel(out Microsoft.Xna.Framework.Graphics.VertexBuffer vb, out Microsoft.Xna.Framework.Graphics.IndexBuffer ib)
        {
            VertexPositionColor[] vertices = new VertexPositionColor[8]
                {
                    new VertexPositionColor(new Vector3(-1f, 1f, -1f), Color.White),
                    new VertexPositionColor(new Vector3(1f, 1f, -1f), Color.White),
                    new VertexPositionColor(new Vector3(-1f, -1f, -1f), Color.White),
                    new VertexPositionColor(new Vector3(1f, -1f, -1f), Color.White),
                    new VertexPositionColor(new Vector3(-1f, 1f, 1f), Color.White),
                    new VertexPositionColor(new Vector3(1f, 1f, 1f), Color.White),
                    new VertexPositionColor(new Vector3(-1f, -1f, 1f), Color.White),
                    new VertexPositionColor(new Vector3(1f, -1f, 1f), Color.White)
                    //new VertexPositionColor(new Vector3(-0.1f, 0.1f, -0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(0.1f, 0.1f, -0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(-0.1f, -0.1f, -0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(0.1f, -0.1f, -0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(-0.1f, 0.1f, 0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(0.1f, 0.1f, 0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(-0.1f, -0.1f, 0.1f), Color.White),
                    //new VertexPositionColor(new Vector3(0.1f, -0.1f, 0.1f), Color.White)
                };
            vb = new VertexBuffer(CDare.Instance.GraphicsDevice, typeof(VertexPositionColor), vertices.Length, BufferUsage.None);
            vb.SetData<VertexPositionColor>(vertices);

            short[] indices = new short[36]
            { 0, 1, 3, 0, 3, 2,
              3, 1, 7, 1, 5, 7,
              4, 7, 5, 4, 6, 7,
              6, 4, 2, 4, 0, 2,
              0, 4, 5, 0, 5, 1,
              2, 3, 6, 3, 7, 6};
            ib = new IndexBuffer(CDare.Instance.GraphicsDevice, IndexElementSize.SixteenBits, indices.Length, BufferUsage.None);
            ib.SetData<short>(indices);
        }

        #endregion

    }
}
